from yant import *

class BufferedReader:
    def __init__(self, buf):
        self._buffer = buf[:]
        self._pos = 0
        
    def eof(self):
        return self._pos >= len(self._buffer)
        
    def peek(self):
        return self._buffer[ self._pos ] 
    
    def pop(self):
        char = self.peek()
        self._pos = self._pos + 1
        return char

    def getLine(self):
         prev = self._buffer[:self._pos+1]
         post = self._buffer[self._pos:]
#         print prev
#         print '-' * 40
#         print post
         line = prev.split('\n')[-1] + post.split('\n')[0]
#         psplit = prev.split('\n')
#         if len(psplit) > 1:
#             line = prev.split('\n')[-2]
#         else:
#             line = prev.split('\n')[-1]
#         print '=' * 40
#         print line
         return line
    
class BufferedFileReader(BufferedReader):
    def __init__(self, filename, stdlib = None):
        file = open(filename, "r")
        buf = file.read()
        file.close()

        if stdlib:
            flib = open(stdlib, "r")
            libbuf = flib.read()
            flib.close()
            buf = libbuf + buf

        BufferedReader.__init__(self, buf)

class Lexer:
    
    def __init__(self, dna, reader):
        self.dna = dna
        self.reader = reader
        
    def feed(self, ch):
        if not self.dna.feed(ch):
            raise Exception, "Automata is stuck after: '" + ch + "'" + " in state " + self.dna._currentState._name
        
        if self.dna.getCurrentState().isSink():
            return True
        else:
            return False
        
    def nextToken(self):
        tokenValue = ""
        
        while True:
            if not self.reader.eof():
                char = self.reader.peek()
            else:
                char = ""
    
            accepted = self.feed(char)
            
            if not self.dna.getCurrentState().isLookAhead():
                if not self.dna.getCurrentState().isSilent(): # hack: do not include theese separators in token values
                    tokenValue = tokenValue + char
                self.reader.pop()
                
            if accepted:
                savedState = self.dna.getCurrentState()
                self.dna.reset()
                return TSymbol(savedState.getName()), tokenValue

    def getLine(self):
        return self.reader.getLine()

class State:
    def __init__(self, name, sink = False, ahead = False, silent=False):
        self._name = name
        self._sink = sink
        self._ahead = ahead
        self._silent = silent
        
    def getName(self):
        return self._name
    
    def isSink(self):
        return self._sink
    
    def isLookAhead(self):
        return self._ahead
    
    def isSilent(self):
        return self._silent
    
    def __hash__(self):
        return hash( self._name )
    
    def __eq__(self, state):
        if state == None:
            return False
        
        return self._name == state.getName()
    
class DeterministicAutomata:
    def __init__(self):
        self._states = {} # for each state store list of transitions
        self._currentState = None 
        self._sourceState = None
        
    def getCurrentState(self):
        return self._currentState
        
    def addTransition(self, left, right, condition):
        if self._currentState == None:
            self._currentState = left
        
        if self._sourceState == None:
            self._sourceState = left
    
        if not self._states.has_key(left):
            self._states [ left ] = []
            
        self._states [ left ].append( (condition, right) )
        
    def reset(self):
        self._currentState = self._sourceState
        
    def _matches(self, condition, input):
        if condition == "EVERYTHING":
            return True
        elif condition == "ANY_DIGIT" and str.isdigit(input):
            return True
        elif condition == "ANY_ALPHA" and str.isalpha(input):
            return True
        elif condition == "ANY_ALNUM" and str.isalnum(input):
            return True
        elif condition == "ANY_SEPARATOR" and input in '; (),\\n[]':
            return True
        else:
            return condition == input
        
    # True if input is accepted
    def feed(self, inp):
        if not self._states.has_key(self._currentState):
            print "WTF??! now in sink state, ", self._currentState._name
        
        for (cond, right) in self._states [ self._currentState ]:
            if self._matches(cond, inp):
                #print "use rule:", cond, "new state is", right._name, "input is", inp
                self._currentState = right
                return True
            
        return False
                
        
